/**
 * Class View
 * This class manipulate HTMLElement
 * Example:
 *  //by html element id
 *  var element = new View("element-id");
 *  //by html node
 *  var element = new View(document.body);
 */
View = (function() {
    /**
     * This constructor get element by id or use already get and create classList object
     * @param element {String|HTMLElement}
     * @constructor
     */
    function View(element) {
        if (typeof element === "string") {
            this.getById(element);
        } else {
            this.element = element;
        }
        this.childs = [];
        if (this.element) {
            this.classList = this.element.classList;
        }
    }
    /**
     * Method that get element by id and create classList
     * @param id {String}
     */
    View.prototype.getById = function(id) {
        this.element = document.getElementById(id);
        this.classList = this.element.classList;
    };
    /**
     * Add css class to element or classes separated by spece(s)
     * @param name {String} Css Class(es) Name
     */
    View.prototype.addClass = function(name) {
        var collection, item, _i, _len;

        if (!this.classList) return this;
        if (!name) return this;

        name = name.replace(/^\s+|\s+$/g, '');
        collection = name.split(/\s+/);

        for (_i = 0, _len = collection.length; _i < _len; _i++) {
            item = collection[_i];
            this.classList.add(item);
        }
		return this;
    };
    /**
     * Remove css class from element or classes separated by spece(s)
     * @param name {String} Css Class(es) Name
     */
    View.prototype.removeClass = function(name) {
        var collection, item, _i, _len;
        if (name) {
            name = name.replace(/^\s+|\s+$/g, '');
        }
        collection = name.split(/\s+/);
        if (this.classList) {
            for (_i = 0, _len = collection.length; _i < _len; _i++) {
                item = collection[_i];
                try{
                    this.classList.remove(item);
                }
                catch(e){}
            }
        }
    };
    /**
     * Check if this element already has css class or any of classes
     * @param name {String} Css Class(es) Name
     * @returns {boolean}
     */
    View.prototype.hasClass = function(name) {
        var collection, item, _i, _len,
            has = false;
        if (!this.classList) return;
        if (!name) return;
        name = name.replace(/^\s+|\s+$/g, '');
        collection = name.split(/\s+/);
        for (_i = 0, _len = collection.length; _i < _len; _i++) {
            item = collection[_i];
            has = this.classList.contains(item);
        }
        return has;
    };
    /**
     * Get Closest by class name
     * @param name {String} css class name
     * @param el {HTMLElement} html element
     * @returns {HTMLElement}
     */
    View.prototype.closestByClassName = function(name, el) {
        if (el == null) {
            el = this.element;
        }
        if (el.classList.contains(name)) {
            return el;
        } else {
            if (el.parentElement) {
                return this.closestByClassName(name, el.parentElement);
            }
        }
    };
    /**
     * Set or get attributes
     * @param attrs {String|Object} name of attribute if we want to get attribute value or object if we want to set attribute
     * @returns {*} Attribute value or false if we set
     */
    View.prototype.attr = function(attrs) {
        var attr;
        if (typeof attrs === "string") {
            if (this.element.hasOwnProperty(attr)) {
                return this.element[attrs];
            } else {
                return this.element.getAttribute(attrs);
            }
        } else {
            if (attrs instanceof Object) {
                for (attr in attrs) {
                    if (this.element.hasOwnProperty(attr)) {
                        this.element[attr] = attrs[attr];
                    } else {
                        this.element.setAttribute(attr, attrs[attr]);
                    }
                }
            }
        }
        return false;
    };
    /**
     * Set or get property
     * @param props {String|Object} name of property if we want to get property value or object if we want to set property
     * @returns {*} property value or false
     */
    View.prototype.prop = function(props) {
        var prop;
        if (typeof props === "string") {
            return this.element[props];
        } else {
            if (props instanceof Object) {
                for (prop in props) {
                    this.element[prop] = props[prop];
                }
            }
        }
        return false;
    };
    /**
     * Remove attribute by name
     * @param attr {String} attribute name
     */
    View.prototype.removeAttr = function(attr) {
        if (this.element.hasOwnProperty(attr)) {
            this.element[attr] = null;
        } else {
            this.element.removeAttribute(attr);
        }
    };
    /**
     * Get parent element
     * @param ishtml {Boolean} if html or view
     * @returns {HTMLElement|View}
     */
	View.prototype.parent = function(ishtml){
		if(ishtml){
			return this.element.parentElement;
		}else{
			return new View(this.element.parentElement);
		}
	};
    /**
     * Get element or view by css selector
     * @param selector {String} css selector
     * @param html {Boolean} if html or view
     * @returns {View|HTMLElement}
     */
    View.prototype.select = function(selector, html){
        var _el = this.element.querySelector(selector);
        if(!html){
            if(_el) _el = new View(_el);
        }
        return _el;
    };
    /**
     * Get elements or views by css selector
     * @param selector {String} css selector
     * @param html {Boolean} if html or view
     * @returns {Views|HTMLElement}
     */
    View.prototype.selectAll = function(selector, html){
        var _els;
        if(html){
            _els = this.element.querySelectorAll(selector);
        }else{
            _els = new Views(this.element.querySelectorAll(selector));
        }
        return _els;
    };
    /**
     * Remove element from html
     * @returns {*}
     */
	View.prototype.remove = function(){
		return this.parent(true).removeChild(this.element);
	};
    /**
     * Append child to this element
     * @param child {HTMLElement|View} child element or view
     * @returns {View} child view
     */
    View.prototype.append = function(child) {
        this.childs.push(!(child instanceof View) ? new View(child) : child);
        this.element.appendChild(child.element || child);
        return this.childs[this.childs.length - 1];
    };
    /**
     * Set or get inner html of this view
     * @param [html] {String} string html or not if you want to get the html
     * @returns {*}
     */
    View.prototype.html = function(html) {
        if(html) return this.element.innerHTML = html;
        return this.element.innerHTML;
    };
    /**
     * Set or get inner text of this view
     * @param [text] {String} string text or not if you want to get the text
     * @returns {*}
     */
    View.prototype.text = function(text) {
        if(text) return this.element.innerText = text;
        return this.element.innerText;
    };
    /**
     * Get all childrend html element
     * @returns {HTMLElement[]} children
     */
    View.prototype.childrens = function() {
        return this.element.children;
    };
    /**
     * Return if this element is in dom or is still not inserted
     * @returns {boolean}
     */
    View.prototype.inDOM = function() {
        return !!this.element.parentElement;
    };
    /**
     * Show element whit display or visibility
     * @param [visibility] {Boolean} to use visibility or not
     */
	View.prototype.show = function(visibility){
		if(visibility){
            this.style({visibility: ""});
        }else{
            this.style({display: ""});
        }
	};
    /**
     * Hide element whit display or visibility
     * @param [visibility] {Boolean} to use visibility or not
     */
	View.prototype.hide = function(visibility){
        if(visibility){
            this.style({visibility: "hidden"});
        }else{
            this.style({display: "none"});
        }
	};
    /**
     * Add event listener
     * @param event {String} event name
     * @param callback {Function} event callback
     * @returns {*}
     */
    View.prototype.addEvent = function(event, callback) {
        if ("addEventListener" in window) {
            return this.element.addEventListener(event, callback);
        } else {
            return this.element.attachEvent("on" + event, callback);
        }
    };
    /**
     * Remove event listener
     * @param event {String} event name
     * @param callback {Function} event callback
     * @returns {*}
     */
    View.prototype.removeEvent = function(event, callback) {
        if ("removeEventListener" in window) {
            return this.element.removeEventListener(event, callback);
        } else {
            return this.element.detachEvent("on" + event, callback);
        }
    };
    /**
     * Bind event with Touch
     * @param event {String} event name
     * @param callback {Function} event callback
     * @param [bubble] {Boolean} to have bubble or not
     * @returns {*}
     */
    View.prototype.bind = function(event, callback, bubble) {
        return Touch.observe(this.element, event, callback, bubble);
    };
    /**
     * Un Bind event with Touch
     * @param event {String} event name
     * @param callback {Function} event callback
     * @returns {*}
     */
    View.prototype.unbind = function(event, callback) {
        return Touch.stopObserving(this.element, event, callback);
    };
    /**
     * Set or get html style
     * @param styles {String|Object} string to get some value or object to set
     * @returns {String} style value
     */
    View.prototype.style = function(styles) {
        var key;

        if(styles instanceof String) return this.element.style[styles];

        this.styles = styles;
        for (key in this.styles) {
            this.element.style[key] = this.styles[key];
        }
    };

    return View;

})();